#include "adc.hpp"
#include "system.h"
#include "apm32a10x_adc.h"
#include "apm32a10x_rcm.h"
#include "apm32a10x_gpio.h"
#include "apm32a10x_dma.h"

#include "stdio.h"

static const struct {
    ADC_T *adc;
    PeripheralClock_t adc_clk;
    DMA_Channel_T *adc_dma_channel;

    ADC_CHANNEL_T adc_ch_hall;
    GPIO_T *port_hall;
    PeripheralClock_t port_clk_hall;
    GPIO_PIN_T pin_hall;
    
    ADC_CHANNEL_T adc_ch_pressure_sensor;
    GPIO_T *port_pressure_sensor;
    PeripheralClock_t port_clk_pressure_sensor;
    GPIO_PIN_T pin_pressure_sensor;

    ADC_CHANNEL_T adc_ch_temprature;
}__hardware_resource = {
    .adc = ADC1,
    .adc_clk = CLK_ADC1,
    .adc_dma_channel = DMA1_Channel1,
    .adc_ch_hall = ADC_CHANNEL_0,
    .port_hall = GPIOA,
    .port_clk_hall = CLK_GPIOA,
    .pin_hall = GPIO_PIN_0,
    .adc_ch_pressure_sensor = ADC_CHANNEL_1,
    .port_pressure_sensor = GPIOA,
    .port_clk_pressure_sensor = CLK_GPIOA,
    .pin_pressure_sensor = GPIO_PIN_1,
    .adc_ch_temprature = ADC_CHANNEL_TEMP_SENSOR,
};

volatile uint16_t __adc_data[3];

bool ADCUtil::init(){
    GPIO_Config_T gpio_configuration;
    ADC_Config_T adc_configuration;

    System_setPeripheralClockEnable(__hardware_resource.port_clk_hall,TRUE);
    System_setPeripheralClockEnable(__hardware_resource.port_clk_pressure_sensor,TRUE);
    System_setPeripheralClockEnable(__hardware_resource.adc_clk,TRUE);

    /* Hall. */
    gpio_configuration.pin = __hardware_resource.pin_hall;
    gpio_configuration.mode = GPIO_MODE_ANALOG;
    gpio_configuration.speed = GPIO_SPEED_50MHz;
    GPIO_Config(__hardware_resource.port_hall,&gpio_configuration);

    /* Pressure sensor. */
    gpio_configuration.pin = __hardware_resource.pin_pressure_sensor;
    GPIO_Config(__hardware_resource.port_pressure_sensor,&gpio_configuration);

    RCM_ConfigADCCLK(RCM_PCLK2_DIV_4);
    /* ADC init. */
    ADC_EnableTempSensorVrefint(__hardware_resource.adc);
    ADC_Reset(__hardware_resource.adc);
    ADC_ConfigStructInit(&adc_configuration);
    adc_configuration.mode = ADC_MODE_INDEPENDENT;
    adc_configuration.scanConvMode = ENABLE;
    adc_configuration.continuosConvMode = ENABLE;
    adc_configuration.externalTrigConv = ADC_EXT_TRIG_CONV_None;
    adc_configuration.dataAlign = ADC_DATA_ALIGN_RIGHT;
    adc_configuration.nbrOfChannel = 3;
    ADC_Config(__hardware_resource.adc,&adc_configuration);
    /* ADC channel convert configration. */
    ADC_ConfigRegularChannel(__hardware_resource.adc,__hardware_resource.adc_ch_hall,1,ADC_SAMPLETIME_28CYCLES5);
    // ADC_ConfigRegularChannel(__hardware_resource.adc,__hardware_resource.adc_ch_pressure_sensor,2,ADC_SAMPLETIME_13CYCLES5);
    // ADC_ConfigRegularChannel(__hardware_resource.adc,__hardware_resource.adc_ch_temprature,3,ADC_SAMPLETIME_13CYCLES5);

    // ADC_EnableTempSensorVrefint(__hardware_resource.adc);

    ADC_EnableDMA(__hardware_resource.adc);
    ADC_Enable(__hardware_resource.adc);

    /* Reset adc calibration register. */
    ADC_ResetCalibration(__hardware_resource.adc);
    while( ADC_ReadResetCalibrationStatus(__hardware_resource.adc) );

    /* Start adc calibration. */
    ADC_StartCalibration(__hardware_resource.adc);
    while( ADC_ReadCalibrationStartFlag(__hardware_resource.adc) );

    ADC_EnableSoftwareStartConv(__hardware_resource.adc);

    /* Initialize DMA. */
    DMA_Config_T dma_configuration;
    System_setPeripheralClockEnable(CLK_DMA1,TRUE);
    DMA_Reset(__hardware_resource.adc_dma_channel);
    DMA_Disable(__hardware_resource.adc_dma_channel);
    dma_configuration.peripheralBaseAddr = ((uint32_t)&__hardware_resource.adc->REGDATA);
    dma_configuration.memoryBaseAddr = (uint32_t)__adc_data;
    dma_configuration.dir = DMA_DIR_PERIPHERAL_SRC;
    dma_configuration.bufferSize = 3;
    dma_configuration.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
    dma_configuration.memoryInc = DMA_MEMORY_INC_ENABLE;
    dma_configuration.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
    dma_configuration.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
    dma_configuration.loopMode = DMA_MODE_CIRCULAR;
    dma_configuration.priority = DMA_PRIORITY_HIGH;
    dma_configuration.M2M = DMA_M2MEN_DISABLE;

    DMA_Config(__hardware_resource.adc_dma_channel,&dma_configuration);
    DMA_Enable(__hardware_resource.adc_dma_channel);

    return true;
}

double ADCUtil::getChannel(Channel channel){
    int channel_num = 0;
    switch ( channel ){
        case Channel::Hall: channel_num = 0; break;
        case Channel::PressureSensor: channel_num = 1; break;
        case Channel::Temprature: channel_num = 2; break;
    }
    return __adc_data[channel_num] * 4980.0 / 4095.0 / 1000;
}

double ADCUtil::getTemperature(void){
    uint16_t temp_value = __adc_data[2];
    printf("Temp raw value:%d\r\n",temp_value);
    return (1.42 - temp_value * 3.3 / 4096) * 1000 / 4.3 + 25;
}
